////////////////////////////////////////////////////////////////////////////////
// The Loki Library
// Copyright (c) 2006 by Guillaume Chatelet
//
// Code covered by the MIT License
//
// Permission to use, copy, modify, distribute and sell this software for any
// purpose is hereby granted without fee, provided that the above copyright
// notice appear in all copies and that both that copyright notice and this
// permission notice appear in supporting documentation.
//
// The authors make no representations about the suitability of this software
// for any purpose. It is provided "as is" without express or implied warranty.
//
// This code DOES NOT accompany the book:
// Alexandrescu, Andrei. "Modern C++ Design: Generic Programming and Design
//     Patterns Applied". Copyright (c) 2001. Addison-Wesley.
//
////////////////////////////////////////////////////////////////////////////////

// $Id: SPCachedFactory.h 896 2008-08-08 22:20:05Z syntheticpp $

#ifndef SPCACHEDFACTORY_H_
#define SPCACHEDFACTORY_H_

/**
 * This file is intented to be used if you want a CachedFactory with
 * a SmartPointer encapsulation policy.
 * It as been defined in a separate file because of the many introduced
 * dependencies (SmartPtr.h would depend on Functor.h and CachedFactory.h
 * would depend on SmartPtr.h). By defining another header you pay for those
 * extra dependencies only if you need it.
 *
 * This file defines FunctionStorage a new SmartPointer storage policy and
 * SmartPointer a new CachedFactory encapsulation policy.
 */

#include <loki/Functor.h>
#include <loki/SmartPtr.h>
#include <loki/CachedFactory.h>

namespace Loki
{

////////////////////////////////////////////////////////////////////////////////
///  \class FunctionStorage
///
///  \ingroup  SmartPointerStorageGroup
///  \brief Implementation of the StoragePolicy used by SmartPtr.
///
///  This storage policy is used by SmartPointer CachedFactory's encapsulation
///  policy. It's purpose is to call a Functor instead of deleting the
///  underlying pointee object. You have to set the callback functor by calling
///  SetCallBackFunction(const FunctorType &functor).
///
///  Unfortunately, the functor argument is not a reference to the SmartPtr but
///  a void *. Making functor argument a reference to the pointer would require
///  the FunctionStorage template to know the full definition of the SmartPtr.
////////////////////////////////////////////////////////////////////////////////

template <class T>
class FunctionStorage
{
public:
	/// the type of the pointee_ object
	typedef T *StoredType;
	/// type used to declare OwnershipPolicy type.
	typedef T *InitPointerType;
	/// type returned by operator->
	typedef T *PointerType;
	/// type returned by operator*
	typedef T &ReferenceType;
	/// type of the Functor to set
	typedef Functor< void , Seq< void * > > FunctorType;

	FunctionStorage() : pointee_(Default()), functor_()
	{}

	// The storage policy doesn't initialize the stored pointer
	//     which will be initialized by the OwnershipPolicy's Clone fn
	FunctionStorage(const FunctionStorage &rsh) : pointee_(0), functor_(rsh.functor_)
	{}

	template <class U>
	FunctionStorage(const FunctionStorage<U>& rsh) : pointee_(0), functor_(rsh.functor_)
	{}

	FunctionStorage(const StoredType &p) : pointee_(p), functor_() {}

	PointerType operator->() const
	{
		return pointee_;
	}

	ReferenceType operator*() const
	{
		return *pointee_;
	}

	void Swap(FunctionStorage &rhs)
	{
		std::swap(pointee_, rhs.pointee_);
		std::swap(functor_, rhs.functor_);
	}

	/// Sets the callback function to call. You have to specify it or
	/// the smartPtr will throw a bad_function_call exception.
	void SetCallBackFunction(const FunctorType &functor)
	{
		functor_ = functor;
	}

	// Accessors
	template <class F>
	friend typename FunctionStorage<F>::PointerType GetImpl(const FunctionStorage<F>& sp);

	template <class F>
	friend const typename FunctionStorage<F>::StoredType &GetImplRef(const FunctionStorage<F>& sp);

	template <class F>
	friend typename FunctionStorage<F>::StoredType &GetImplRef(FunctionStorage<F>& sp);

protected:
	// Destroys the data stored
	// (Destruction might be taken over by the OwnershipPolicy)
	void Destroy()
	{
		functor_(this);
	}

	// Default value to initialize the pointer
	static StoredType Default()
	{
		return 0;
	}

private:
	// Data
	StoredType pointee_;
	FunctorType functor_;
};

template <class T>
inline typename FunctionStorage<T>::PointerType GetImpl(const FunctionStorage<T>& sp)
{
	return sp.pointee_;
}

template <class T>
inline const typename FunctionStorage<T>::StoredType &GetImplRef(const FunctionStorage<T>& sp)
{
	return sp.pointee_;
}

template <class T>
inline typename FunctionStorage<T>::StoredType &GetImplRef(FunctionStorage<T>& sp)
{
	return sp.pointee_;
}

/**
 * \class	SmartPointer
 * \ingroup	EncapsulationPolicyCachedFactoryGroup
 * \brief	Encapsulate the object in a SmartPtr with FunctionStorage policy.
 *
 * The object will come back to the Cache as soon as no more SmartPtr are
 * referencing this object. You can customize the SmartPointer with the standard
 * SmartPtr policies (OwnershipPolicy, ConversionPolicy, CheckingPolicy,
 * ConstnessPolicy) but StoragePolicy is forced to FunctionStorage.
 */
template
<
class AbstractProduct,
      template <class> class OwnershipPolicy = RefCounted,
      class ConversionPolicy = DisallowConversion,
      template <class> class CheckingPolicy = AssertCheck,
      template<class> class ConstnessPolicy = LOKI_DEFAULT_CONSTNESS
      >
class SmartPointer
{
private:
	typedef SmartPtr< AbstractProduct,OwnershipPolicy,
	        ConversionPolicy, CheckingPolicy,
	        FunctionStorage, ConstnessPolicy > CallBackSP;
protected:
	typedef CallBackSP ProductReturn;
	SmartPointer() : fun(this, &SmartPointer::smartPointerCallbackFunction) {}
	virtual ~SmartPointer() {}

	ProductReturn encapsulate(AbstractProduct *pProduct)
	{
		CallBackSP SP(pProduct);
		SP.SetCallBackFunction(fun);
		return SP;
	}

	AbstractProduct *release(ProductReturn &pProduct)
	{
		return GetImpl(pProduct);
	}

	const char *name()
	{
		return "smart pointer";
	}

private:
	SmartPointer &operator=(const SmartPointer &);
	SmartPointer(const SmartPointer &);
	void smartPointerCallbackFunction(void *pSP)
	{
		CallBackSP &SP(*reinterpret_cast<CallBackSP *>(pSP));
		ReleaseObject(SP);
	}
	virtual void ReleaseObject(ProductReturn &object)=0;
	const typename CallBackSP::FunctorType fun;
};

} // namespace Loki

#endif /*SPCACHEDFACTORY_H_*/
